#include <string>
#include <vector>
#include <stdlib.h>
//#include "simple_log.h"
#include "redis.h"
#include <iostream>
using namespace std;

Redis::Redis(const string &ip, unsigned int port,const string& passwd ):
    m_conn(NULL),m_conf(ip,port,passwd)
{
}

Redis::~Redis()
{
    release();
}

void Redis::release()
{
    if ( m_conn != NULL )
    {
        redisFree(m_conn);
        m_conn = NULL;
    }
}

bool Redis::Login(redisContext* pconn, const string& passwd)
{
    if ( pconn == NULL )
        return false;

    redisReply* rsp = NULL;
    string cmd = "auth " + passwd;
    rsp = (redisReply*)redisCommand(pconn, cmd.c_str());
	
    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (pconn->err != 0)
    {
        if (rsp != NULL)
        {
            freeReplyObject(rsp);
        }
        return false;
    }

    if (rsp != NULL)
        freeReplyObject(rsp);

    return true;
}

bool Redis::CheckConnStatus(redisContext* pconn,const RedisConnConf& conf)
{
    if (!conf.m_passwd.empty())
    {
        if ( !Login(pconn,conf.m_passwd) )
        {
            return false;
        }
    }
    if (pconn == NULL || pconn->err != 0 )
    {
        return false;
    }

    return true;
}

void Redis::Reconnect()
{
    if (m_conn == NULL)
    {
        m_conn = redisConnect(m_conf.m_ip.c_str(), m_conf.m_port);
        CheckConnStatus(m_conn,m_conf);
    }
    else if (m_conn->err != 0)
    {
        redisFree(m_conn);
        m_conn = redisConnect(m_conf.m_ip.c_str(), m_conf.m_port);
        CheckConnStatus(m_conn,m_conf);
    }
}

void Redis::SetKeyValue(const string& key, const string &value, const string &ttl)
{
    Reconnect();
    
    if (m_conn == NULL)
        return ;

    redisReply* rsp = NULL;
    if ( !ttl.empty() )
    {
        rsp = (redisReply*)redisCommand(m_conn, "SET %b %b EX %s",
            key.c_str(), key.size(), value.c_str(), value.size(), ttl.c_str());
    }
    else
    {
        rsp = (redisReply*)redisCommand(m_conn, "SET %b %b",
            key.c_str(), key.size(), value.c_str(), value.size());
    }
    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (rsp != NULL)
        freeReplyObject(rsp);
}

void Redis::HSetKeyValue(const string& hash, const string& key, const string &value, const string &ttl)
{
    Reconnect();
    if (m_conn == NULL)
        return;

    redisReply* rsp = NULL;
    rsp = (redisReply*)redisCommand(m_conn, "HSET %b %b %b",
            hash.c_str(), hash.size(), key.c_str(), key.size(), value.c_str(), value.size());
    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (rsp != NULL)
        freeReplyObject(rsp);
    
    if ( !ttl.empty() )
    {
        rsp = (redisReply*)redisCommand(m_conn, "EXPIRE %s %s",hash.c_str(),ttl.c_str() );
        if (rsp != NULL)
            freeReplyObject(rsp);
    }
}

bool Redis::Exec(const string& cmd, redisReply **presp)
{
    Reconnect();
    bool bret = false;
    if (m_conn == NULL)
        return bret;
    redisReply* rsp = NULL;
    *presp = NULL;
    rsp = (redisReply*)redisCommand(m_conn, cmd.c_str());
	
    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (m_conn->err != 0)
    {
        if (rsp != NULL)
        {
            freeReplyObject(rsp);
        }
        return bret;
    }

    // presp保存的不释放，由外部释放
    if (rsp != NULL)
    {
        bret = true;
        if ( *presp == NULL )
            *presp = rsp;
        else
            freeReplyObject(rsp);
    }

    return bret;
}

string Redis::HGetKeyValue(const string& hash, const string& key)
{
    Reconnect();
    if (m_conn == NULL)
        return "";
    
    redisReply* rsp = NULL;
    rsp = (redisReply*)redisCommand(m_conn, "HGET %b %b",
            hash.c_str(),
            hash.size(),
            key.c_str(),
            key.size());
	
    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (m_conn->err != 0)
    {
        if (rsp != NULL)
        {
            freeReplyObject(rsp);
        }
        return "";
    }
    
    if (rsp != NULL)
    {
        if (rsp->type == REDIS_REPLY_STRING)
        {
            string tmp(rsp->str);
            freeReplyObject(rsp);
            return tmp;
        }
        freeReplyObject(rsp);
    }
    return "";
}

void Redis::HGetValue(const string& table, vector<string> &result)
{
    Reconnect();
    if (m_conn == NULL)
        return;

    redisReply* rsp = NULL;
    rsp = (redisReply*)redisCommand(m_conn, "HVALS %b %b",
            table.c_str(),
            table.size());

    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (m_conn->err != 0)
    {
        if (rsp != NULL)
        {
            freeReplyObject(rsp);
        }
        return ;
    }

    if (rsp != NULL)
    {
        if (rsp->type == REDIS_REPLY_ARRAY)
        {
            result.resize(rsp->elements);

            for (int i=0; i<(int)(rsp->elements); i++)
            {
                if (REDIS_REPLY_STRING == rsp->element[i]->type)
                    result[i].assign(rsp->element[i]->str);
            }
        }
        freeReplyObject(rsp);
    }
}

string Redis::GetKey(const string& key)
{
    Reconnect();
    if (m_conn == NULL)
        return "";
    
    redisReply* rsp = NULL;
    rsp = (redisReply*)redisCommand(m_conn, "GET %b", key.c_str(), key.size());
    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (m_conn->err != 0)
    {
        if (rsp != NULL)
            freeReplyObject(rsp);
        return "";
    }

    if (rsp != NULL)
    {
        if (rsp->type == REDIS_REPLY_STRING)
        {
            string tmp(rsp->str);
            freeReplyObject(rsp);
            return tmp;
        }
        freeReplyObject(rsp);
    }

    return "";
}

void Redis::KeyPattern(const string& key_pat, vector<string>& results)
{
    Reconnect();
    if (m_conn == NULL)
        return ;
    
    redisReply* rsp = NULL;
    rsp = (redisReply*)redisCommand(m_conn, "KEYS %b", key_pat.c_str(), key_pat.size());
    // m_ctx->err 检测连接中断 或者 server 挂掉
    if (m_conn->err != 0)
    {
        if (rsp != NULL)
        {
            freeReplyObject(rsp);
        }
        return;
    }

    if (rsp != NULL)
    {
        if (rsp->type == REDIS_REPLY_ARRAY)
        {
            results.resize(rsp->elements);
            for (int i=0; i<(int)(rsp->elements); i++)
            {
                if (REDIS_REPLY_STRING == rsp->element[i]->type)
                    results[i] = rsp->element[i]->str;
            }
        }
        freeReplyObject(rsp);
    }
}


long long Redis::GetInt(const string& key)
{
    long long result = 0;
    Reconnect();
    if (m_conn == NULL)
        return result;

    redisReply* rsp = NULL;
    rsp = (redisReply*)redisCommand(m_conn,"GET %b", key.c_str(), key.size());
    
    if (m_conn->err != 0)
    {
        if (rsp != NULL)
        {
            freeReplyObject(rsp);
        }
        return result;
    }

    if (rsp != NULL)
    {
        if (rsp->type == REDIS_REPLY_INTEGER)
        {
            result = rsp->integer;
        }
        else if (rsp->type == REDIS_REPLY_NIL)
        {
            result = 0;
        }
        else if (rsp->type == REDIS_REPLY_STRING)
        {
            result = atoi(rsp->str);
        }
        freeReplyObject(rsp);
    }
    return result;
}



